package taller2.model.graph.algorithms;

import java.util.ArrayList;
import java.util.Stack;

import taller2.interfaces.Connection;
import taller2.interfaces.Graph;
import taller2.interfaces.Node;
import taller2.utils.Constants;
import taller2.utils.Constants.Colores;

public class StronglyConnectedApplicator extends AlgorithmApplicatorImpl{
	private int cont;
	private int index;
	private Stack<Node> stack;
	private ArrayList<ArrayList<Node>> SConnectedComp,res;
	private boolean directed;
	private static final String path = Constants.savedGraphsPath + "ComponentesFuertementeConexas";		
	
	public StronglyConnectedApplicator(){
		super();
		directed = false;
	}
	
	// applies the algorithm to the graph
	// and stores the path's and pseudocode lines
	public void solveProblem(Graph g){
    	cont = 0;
		
		if(noGraph(g) || !noGraphPaths() || !noPseudocodeLines())
			return;

		g.unselectConnections();		
		g.unselectNodes();		
		g.killFluxes();
		g.killWeights();
		directed = g.isDirected();

		ArrayList<Node> nodes = g.getNodes();
		for(int i=0 ; i<nodes.size(); i++){
			Node node = nodes.get(i); 
			node.setId(i);
			node.setMarker(null);
		}
		
		g.save(path);

		g.save(path+cont);
		
		guardar(path+cont,0);	
		guardar(path+cont,2);	
		guardar(path+cont,3);	
		guardar(path+cont,4);	

	    cont++;

        index = 0;
        SConnectedComp = new ArrayList<ArrayList<Node>>();
        stack = new Stack<Node>();
        tarjan(g);

	    g.unselectConnections();
	    g.unselectNodes();        
        markResults(g,path);
        
        guardar(path,28);
        
		guardar(path,30);
		
		g.load(path);
		
		res = SConnectedComp;
	}
	
	private void markResults(Graph g, String path) {
	    
		cont++;
	    g.save(path+cont);
		guardar(path+cont,28);	
	    
		for(ArrayList<Node> component : SConnectedComp){
			markResult(component);

			cont++;			
			g.save(path+cont);
			guardar(path+cont,28);	
		    
		    g.unselectConnections();
		    g.unselectNodes();
		}
	}

	private void markResult(ArrayList<Node> component){
		for(int i=0; i<component.size() ; i++){
			for(int j=0; j<component.size() ; j++){
				Node start = component.get(i);
				Node end = component.get(j);

				start.select();
				end.select();
			
				start.selectConnectionsWith(end);
			}
		}
	}

	private void tarjan(Graph g){	
		g.save(path+cont);
		guardar(path+cont,6);	
		
		for(Node node : g.getNodes()){
			if(node.getMarker() == null || node.getMarker().isEmpty()){
				guardar(path+cont,7,node);	
				guardar(path+cont,8,node);	
				strongConnect(g,node);
			}
		}
	}
	
	private void strongConnect(Graph g,Node node){
		guardar(path+cont,10,node);	

		node.select();

		cont++;
		g.save(path+cont);
		guardar(path+cont,11,node);	

		node.setMarker(String.valueOf(index)+"/"+String.valueOf(index));
		index++;
		stack.push(node);
		
		cont++;
		g.save(path+cont);
		guardar(path+cont,12,node);	
		guardar(path+cont,13,node);	
		guardar(path+cont,14,node);	
		
		for (Connection connection : node.getConnections()){
			Node other = connection.getOtherNode();

			other.unselect();
			other.changeColor(Colores.purple);
			
			cont++;
			g.save(path+cont);
			guardar(path+cont,16,node);	
			guardar(path+cont,17,node);
			
			if(getIndex(other) == -1){
				guardar(path+cont,18,node);
				strongConnect(g,other);
				
				int ll = Math.min(getLowLink(node),getLowLink(other));
				setLowLink(node, ll);
				
				cont++;
				g.save(path+cont);
				guardar(path+cont,19,node);
			}else{
				other.select();
				guardar(path+cont,20,node);
				guardar(path+cont,21,node);
				if(stack.contains(other)){
					int ll = Math.min(getLowLink(node),getIndex(other));
					setLowLink(node, ll);

					cont++;
					g.save(path+cont);
					guardar(path+cont,22,node);
				}
			}
		}
		
		cont++;
		g.save(path+cont);	
		guardar(path+cont,24,node);
		if(getLowLink(node) == getIndex(node)){
			ArrayList<Node> subset = new ArrayList<Node>();
			Node other = null;
			
			while(node != other){
				other = stack.pop();
				subset.add(other);
			}
			SConnectedComp.add(subset);			
			markResult(subset);
			
			cont++;
			g.save(path+cont);	
			guardar(path+cont,25,node);
			guardar(path+cont,26,node);
		}
	}
	
	private int getIndex(Node node){
		String indexAndLowlink = node.getMarker();

		if(indexAndLowlink == null)
			return -1;

		String[] ind = indexAndLowlink.split("/",0);
	
		if(ind == null)
			return -1;
		
		return Integer.valueOf(ind[0]);
	}

	private void setLowLink(Node node,int ll){
		int ind = getIndex(node);
		String marker = String.valueOf(ind) + "/" + String.valueOf(ll);
		node.setMarker(marker);
	}

	
	private int getLowLink(Node node){
		String indexAndLowlink = node.getMarker();

		if(indexAndLowlink == null)
			return -1;

		String[] ll = indexAndLowlink.split("/",0);
		
		if(ll == null)
			return -1;
		
		return Integer.valueOf(ll[1]);
	}	
	
    private void guardar(String pathGrafo,int lineaPseudocodigo){
        graphPaths.add(pathGrafo);
    	pseudocodeLines.add(lineaPseudocodigo);
    	auxText.add(formatStack());
    }

    private void guardar(String pathGrafo,int lineaPseudocodigo,Node node){
        graphPaths.add(pathGrafo);
    	pseudocodeLines.add(lineaPseudocodigo);
    	auxText.add(" n: "+ node.getID() + formatStack());
    }

    
    public String formatStack(){
    	if (stack == null)
    		return "";
    	
    	String stk = "\n";
    	for (int i=stack.size()-1; i>=0; i--){
    		Node node = stack.get(i);
    		stk += node.getID() + "\n";
    	}
    	
    	return stk;
    }
    
    public String getAuxText(){
    	if(auxText.isEmpty())
    		return "";
    	String ret = "Stack: ";
    	return ret + auxText.get(position);
    }
    
	public void save(String path){
	};

	public void load(String path){
	}

	@Override
	public int getHighlightedWordNumber() {
		return -1;
	}

	public boolean wasDirected() {
		return directed;
	}

	public String getStartingGraphPath() {
		if(graphPaths == null || graphPaths.isEmpty())
			return "";
		return graphPaths.get(0);
	}

	public ArrayList<ArrayList<Node>> getComponents() {
		return res;
	}
}
